# Created by Gao Song on 2019-04-18
import lqu
from core.factory import create_object
from event import EventEngine
from scheduler.scheduler import BaseScheduler
from time import sleep
from datetime import time, datetime

from fucker import FuckerStatus
import logging

# import mysite.wsgi

from mysite.lqcon.managements import AccountManagement, SchedulerStatusManagement
from event.event_def import EVENT_TRADE
from core.constant import Direction
from gateway.utils import BrokerAccountInfo

logger = logging.getLogger('scheduler')


class FutureFuckerScheduler(BaseScheduler):
    EVENT_ENGINE_INTERVAL = 5

    def __init__(self, **kwargs):
        super().__init__()

        self.time_table = kwargs.get('time_table', {})
        self.extra_fucker_args = kwargs.get('extra_fucker_args')

        self.event_engine = EventEngine(self.EVENT_ENGINE_INTERVAL)
        self.event_engine.start()

        self.fucker = None
        self.gateway = None

        self.gateway_ready = False

        self.night_prep = self.time_table.get('night_prep', time(20, 50))
        self.night_start = self.time_table.get('night_start', time(21, 0, 0))
        self.night_end = self.time_table.get('night_end', time(23, 50))

        self.morning_prep = self.time_table.get('morning_prep', time(8, 50))
        self.morning_start = self.time_table.get('morning_start', time(9, 0, 0))
        self.morning_end = self.time_table.get('morning_end', time(14, 50))

        self.account_name = kwargs['account_name']
        self.account = AccountManagement.get_account_by_name(self.account_name)

        self.scheduler_type = 'fucker'
        self.scheduler_name = self.account_name
        self.last_err = ''

        self.MAX_ERROR_COUNT = 300
        self.exit_time = None

    def set_fucker_task(self):
        pct = self.account.get_latest_result()
        tmv = self.account.nav
        price = lqu.get_current_price(list(pct.keys()))
        pos = lqu.pct_to_pos(tmv, pct, price)
        pos = lqu.future_pos_limit(pos)
        pos = lqu.min_hands_limit(pos)
        self.fucker.set_task(pos)

    def start_fucker(self):
        self.fucker.start()

    def heartbeat(self, ts):
        if self.gateway is None or not self.gateway.is_connected:
            status = '未连接'
        else:
            status = f'已连接|{self.fucker.status.value}'
            if self.gateway.last_err != '' and self.gateway.last_err != self.last_err:
                self.last_err = self.gateway.last_err
        self.report_status(status, self.last_err)

    def report_status(self, status, err):
        try:
            SchedulerStatusManagement.update(
                scheduler_type=self.scheduler_type,
                scheduler_name=self.scheduler_name,
                status=status,
                err=err
            )
        except Exception as e:
            logger.warning(e)

    def on_trade(self, e):
        trade_data = e.data
        if trade_data.orderid not in self.fucker.orders_own:
            return

        symbol = f'{trade_data.symbol}.{trade_data.exchange.value}'
        volume = trade_data.volume
        price = trade_data.price
        if trade_data.direction == Direction.SHORT:
            volume = -volume

        self.account.long(symbol, volume, price)

    def is_fucking_time(self, t):
        return self.morning_start < t < self.morning_end or self.night_start < t

    def check_gateway(self):
        if self.gateway is None:
            self.gateway = create_object('gateway', self.account.gateway_type, self.event_engine, self.account.name)
            self.gateway.connect(self.account.connection_info)
            self.gateway_ready = False
        elif not self.gateway.is_connected():
            logger.debug(f'md: {self.gateway.md_api.login_status}, td: {self.gateway.td_api.login_status}')
            self.gateway_ready = False
        else:
            self.gateway_ready = True

    def check_fucker(self, ts):
        if self.fucker is not None:
            schedule_fucker_name = 'fucker_' + self.account_name
            if SchedulerStatusManagement.terminate_status(schedule_fucker_name) == 1:
                self.fucker.terminate()
                SchedulerStatusManagement.terminateUpate(schedule_fucker_name, 0)
                logger.info(schedule_fucker_name + " be terminated !!")
                print(schedule_fucker_name + " be terminated !!")
            else:
                print('i am alive')
        t = ts.time()

        if self.fucker is None:
            if self.extra_fucker_args is None:
                self.fucker = create_object('fucker', self.account.fucker_type, self.gateway)
            else:
                self.fucker = create_object('fucker', self.account.fucker_type, self.gateway, **self.extra_fucker_args)
        elif self.fucker.status == FuckerStatus.NOT_INITED:
            self.fucker.init()
        elif self.fucker.status == FuckerStatus.READY:
            broker_pos = self.fucker.position_management.to_series()
            logger.info(f'broker_pos ::{broker_pos}')
            if len(broker_pos != 0):
                sync_pos_res = AccountManagement.sync_all_positions(self.account.name,
                                                                    self.fucker.position_management.to_series())
                logger.info(f'sync_pos_res ::{sync_pos_res}')
                if sync_pos_res is not None:
                    self.account.refresh_from_db()
                    self.last_err = f'{datetime.now()}: 柜台与本地持仓不同，已将本地同步为柜台'
                    logger.warning('found differences in pos')
                    logger.warning(f'\n{sync_pos_res}')
            else:
                logger.warning('柜台持仓为空')

            if self.fucker.position_management.exist_long_short_pos():
                self.last_err = f'{datetime.now()}: 存在多空双向持仓，请检查'
                logger.warning('found long + short in pos')

            self.set_fucker_task()

        elif self.fucker.status == FuckerStatus.SET_TASK:
            if self.is_fucking_time(t):
                self.event_engine.register(EVENT_TRADE, self.on_trade)
                self.fucker.start()

        elif self.fucker.status == FuckerStatus.RUNNING:
            if self.gateway.error_count > self.MAX_ERROR_COUNT:
                self.last_err = f'已累计{self.MAX_ERROR_COUNT}条错误，退出'
                self.fucker.terminate()
            if self.morning_end < t < self.night_prep or self.night_end < t:
                self.fucker.terminate()

    def check_finished(self):
        if self.fucker is not None and self.fucker.status in (FuckerStatus.FINISHED, FuckerStatus.TERMINATED):
            sleep(10)
            try:
                if self.gateway and self.gateway.is_connected:
                    info = BrokerAccountInfo(self.gateway, False)
                    AccountManagement.update_broker_info(self.account_name, info.balance, info.available)
            except Exception as e:
                logger.error(e)

            self.event_engine.stop()
            self.stop()

            status = f'已关闭|{self.fucker.status.value}'
            logger.info('scheduler stopped:' + self.fucker.status.value)
            SchedulerStatusManagement.update(
                scheduler_type=self.scheduler_type,
                scheduler_name=self.scheduler_name,
                status=status
            )

    def process(self, ts):
        if not self.gateway_ready:
            self.check_gateway()
        else:
            self.check_fucker(ts)
            self.check_finished()
